package cn.s1995.sloth.pay.biz.plugin.stripe;

import cn.hutool.json.JSONUtil;
import cn.s1995.sloth.pay.utils.PayConstants;
import com.alibaba.fastjson.JSON;
import com.stripe.Stripe;
import com.stripe.exception.AuthenticationException;
import com.stripe.exception.CardException;
import com.stripe.exception.InvalidRequestException;
import com.stripe.exception.RateLimitException;
import com.stripe.model.Customer;
import com.stripe.model.PaymentIntent;
import com.stripe.model.Refund;
import com.stripe.model.Token;
import com.stripe.model.checkout.Session;
import com.stripe.param.checkout.SessionCreateParams;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

/**
 * StripeUtil
 *
 * @author Lex
 * @description
 * @date 2023/12/26 17:27
 */
@Slf4j
@UtilityClass
public class StripeUtil {

    private static StripeConfig config;

    static {
        try {
            Properties properties = new Properties();
            properties.load(new FileInputStream("stripe.properties"));

            config = new StripeConfig();
            config.setStripeKey(properties.getProperty("stripe.key"));
            config.setStripeWebHookPay(properties.getProperty("stripe.webHook.pay", ""));
            config.setStripeWebHookRefund(properties.getProperty("stripe.webHook.refund", ""));
            config.setStripeWebHookInvoice(properties.getProperty("stripe.webHook.invoice", ""));

        } catch (
                IOException e) {
            log.error("stripe.properties not found - {}", e);
        }
    }

    private static final String STRIPE_KEY = config.getStripeKey();
    public static final String STRIPE_WEB_HOOK_PAY = config.getStripeWebHookPay();
    public static final String STRIPE_WEB_HOOK_REFUND = config.getStripeWebHookRefund();
    public static final String STRIPE_WEB_HOOK_INVOICE = config.getStripeWebHookInvoice();

    public static final String SUCCESS_URL = "/payment/success.html";
    public static final String CANCEL_URL = "/cancel.html";

    /**
     * STRIPE 支付成功
     */
    private static final String STRIPE_SUCCESS = "succeeded";
    /**
     * STRIPE 需要校验卡
     */
    private static final String STRIPE_3D2S = "requires_action";

    public static void main(String[] args) {
        try {
//            Map<String, String> metadata = new HashMap<>();
//            Account account = createStandardAccount("", metadata);
//            AccountLink accountLink = createAccountLink(account.getId());
//            System.out.println(GsonUtil.toJson(accountLink));
//            deleteAccount("acct_1L17J7RO6bX2qkaB");
            createToken("4242424242424242", "12", "25", "123");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建token
     *
     * @param bankNo   卡号
     * @param expMonth 过期月份    eg: 02
     * @param expYear  过期月份    eg: 2020
     * @param cvv      cvv    eg: 122
     * @return 支付token
     */
    @SneakyThrows
    public static Token createToken(String bankNo, String expMonth, String expYear, String cvv) {
        log.info("创建token- banNo={}, expMonth={}, expYear={}, cvv={}", bankNo, expMonth, expYear, cvv);
        try {
            Stripe.apiKey = "sk_test_Rch5Q5x47YPZ0CYxcfvM6dSw";
            Map<String, Object> tokenParams = new HashMap<>();
            Map<String, Object> cardParams = new HashMap<>();
            cardParams.put("number", bankNo);
            cardParams.put("exp_month", expMonth);
            cardParams.put("exp_year", expYear);
            cardParams.put("cvc", cvv);
            tokenParams.put("card", cardParams);
            Token token = Token.create(tokenParams);
            log.info(JSON.toJSONString(token));

            return token;
        } catch (CardException e) {
            log.error("CardException:", e);
            throw e;
        } catch (RateLimitException e) {
            log.error("RateLimitException:", e);
            throw e;
        } catch (InvalidRequestException e) {
            log.error("InvalidRequestException:", e);
            throw e;
        } catch (AuthenticationException e) {
            log.error("AuthenticationException:", e);
            throw e;
        } catch (Exception e) {
            log.error("Exception:", e);
            throw e;
        }
    }

    /**
     * 校验卡的真实性，保存卡的用户信息
     *
     * @param token token
     * @return 保存卡的用户信息
     */
    @SneakyThrows
    public static String setCustomerId(String token) {
        Stripe.apiKey = STRIPE_KEY;
        Map<String, Object> customerParams = new HashMap<>();
        customerParams.put("source", token);
        try {
            Customer customer = Customer.create(customerParams);
            return customer.getId();
        } catch (CardException e) {
            log.error("校验卡异常", e);
            throw e;
        } catch (StripeException e) {
            log.error("StripeException:", e);
            throw e;
        } catch (Exception e) {
            log.error("校验卡异常", e);
            throw e;
        }
    }


    /**
     * 发起支付
     *
     * @param customerId  客户id
     * @param amount      支付金额（单位：元）
     * @param description 描述
     * @param metadata    扩展数据
     * @return
     */
    @SneakyThrows
    public static PaymentIntent paymentIntent(String customerId, BigDecimal amount, String description, String... metadata) {
        log.info("发起支付,customerId={}, 金额={},description={}, metadata={}", customerId, amount, description, metadata);
        try {
            Stripe.apiKey = STRIPE_KEY;
            List<Object> paymentMethodTypes = new ArrayList<>();
            paymentMethodTypes.add("card");
            Map<String, Object> params = new HashMap<>();
            params.put("amount", amount.toString());
            params.put("currency", PayConstants.CURRENCY_USD);
            params.put("description", description);
            params.put("confirm", true);
            params.put("customer", customerId);
            params.put("payment_method_types", paymentMethodTypes);
            if (metadata != null) {
                params.put("metadata", JSONUtil.toJsonStr(metadata));
            }
            PaymentIntent paymentIntent = PaymentIntent.create(params);
            if (paymentIntent != null) {
                return paymentIntent;
            }
        } catch (CardException e) {
            log.error("CardException is: " + e.getCode() + "Message is: " + e.getMessage(), e);
            throw e;
        } catch (StripeException e) {
            log.error("StripeException:", e);
            throw e;
        } catch (Exception e) {
            log.error("Exception:", e);
            throw e;
        }
        return null;
    }


    /**
     * 发起支付-第三方支付
     *
     * @param paymentMethod 支付方式
     * @param amount        金额
     * @param description   描述
     * @param metadata      自定义信息
     * @return PaymentIntent
     */
    @SneakyThrows
    public static PaymentIntent paymentIntentByPaymentMethod(String paymentMethod, BigDecimal amount, String description, String... metadata) {
        log.info("发起支付意图,paymentMethod={}, 金额={}, metadata={}", paymentMethod, amount, metadata);
        try {
            Stripe.apiKey = STRIPE_KEY;
            List<Object> paymentMethodTypes = new ArrayList<>();
            paymentMethodTypes.add("card");
            Map<String, Object> params = new HashMap<>();
            params.put("amount", amount.toString());
            params.put("currency", PayConstants.CURRENCY_USD);
            params.put("description", description);
            params.put("confirm", true);
            params.put("payment_method", paymentMethod);
            params.put("payment_method_types", paymentMethodTypes);
            if (metadata != null) {
                params.put("metadata", JSONUtil.toJsonStr(metadata));
            }
            PaymentIntent paymentIntent = PaymentIntent.create(params);
            if (paymentIntent != null) {
                return paymentIntent;
            }
        } catch (CardException e) {
            log.error("CardException is: " + e.getCode() + "Message is: " + e.getMessage(), e);
            throw e;
        } catch (StripeException e) {
            log.error("StripeException:", e);
            throw e;
        } catch (Exception e) {
            log.error("Exception:", e);
            throw e;
        }
        return null;
    }


    /**
     * stripe退款
     *
     * @param transactionId 交易流水
     * @param amount        金额
     * @param metadata      元数据
     * @return Refund
     */
    @SneakyThrows
    public static Refund refund(String transactionId, Long amount, String... metadata) {
        log.info("退款,交易流水id={}, 退款金额={}", transactionId, amount);
        try {
            Stripe.apiKey = STRIPE_KEY;
            Map<String, Object> refundParams = new HashMap<>();
            refundParams.put("payment_intent", transactionId);
            refundParams.put("amount", amount.toString());
            if (metadata != null) {
                refundParams.put("metadata", JSONUtil.toJsonStr(metadata));
            }
            Refund refund = Refund.create(refundParams);
            log.info("退款结果：" + refund.getStatus() + '-' + refund.toString());
            if (STRIPE_SUCCESS.equals(refund.getStatus().toLowerCase())) {
                return refund;
            }
            return null;
        } catch (StripeException e) {
            log.error("stripe退款失败,交易流水：{}", transactionId, e);
            throw e;
        } catch (Exception e) {
            log.error("stripe退款失败,交易流水：" + transactionId, e);
            throw e;
        }
    }


    /**
     * 创建支付会话（web支付）
     *
     * @param priceId    stripe 价格id
     * @param quantity   数量
     * @return
     */
    @SneakyThrows
    public static Session createSession(String priceId, Long quantity) {
        Stripe.apiKey = STRIPE_KEY;
        SessionCreateParams params =
                SessionCreateParams.builder()
                        .setMode(SessionCreateParams.Mode.PAYMENT)
                        .setSuccessUrl(SUCCESS_URL)
                        .setCancelUrl(CANCEL_URL)
                        .addLineItem(
                                SessionCreateParams.LineItem.builder()
                                        .setQuantity(quantity)
                                        // Provide the exact Price ID (for example, pr_1234) of the product you want to sell
                                        .setPrice(priceId)
                                        .build())
                        .build();
        try {
            Session session = Session.create(params);
            log.info("创建支付会话：{}", JSONUtil.toJsonStr(session));
            return session;
        } catch (StripeException e) {
            log.error("创建支付会话异常：", e);
            throw e;
        } catch (Exception e) {
            log.error("创建支付会话异常：", e);
            throw e;
        }
    }

    /**
     * 取消支付意图
     *
     * @param paymentIntentId 支付意图id
     * @return
     */
    @SneakyThrows
    public static PaymentIntent paymentIntentCancel(String paymentIntentId) {
        Stripe.apiKey = STRIPE_KEY;
        try {
            PaymentIntent paymentIntent =
                    PaymentIntent.retrieve(
                            paymentIntentId
                    );
            PaymentIntent updatedPaymentIntent =
                    paymentIntent.cancel();
            return updatedPaymentIntent;
        } catch (StripeException e) {
            log.error("取消支付意图异常：", e);
            throw e;
        } catch (Exception e) {
            log.error("取消支付意图异常：", e);
            throw e;
        }
    }
}
